package com.jsftoolkit.base;

import java.util.Iterator;

import javax.el.ValueExpression;
import javax.faces.component.ContextCallback;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIComponent;
import javax.faces.component.UIComponentBase;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.FacesEvent;

import com.jsftoolkit.ajax.AjaxContext;
import com.jsftoolkit.utils.Utils;

/**
 * This class may be used as a base class for {@link UIComponent}s, but will
 * typically be used statically, so that components can inherit from other
 * components.
 * <p>
 * By externalizing the code in this class, generated code does not need to be
 * regenerated when the behavior changes.
 * 
 * @author noah
 * 
 */
public abstract class ComponentHelp extends UIComponentBase {

	/**
	 * Helper for component getters. The passed in value should be a component
	 * field that is set by the setter for the property. If value is non-null
	 * and non-empty, it will always be returned. If it is null, a
	 * {@link ValueExpression} for the attribute will be evaluated. If that
	 * value is null, defaultValue is returned, otherwise the value obtained
	 * from the value expression will be returned.
	 * <p>
	 * Note that if value is null, then whatever value is obtained by attempting
	 * to evaluate the value expression, including null, will be used to set the
	 * same attribute on the component.
	 * <p>
	 * This is necessary because there are no guarantees that the passed in
	 * defaultValue will be the name instance on every invocation. e.g. If this
	 * was done, when you called <code>getValue().setSomeProperty(foo)</code>,
	 * subsequent calls to <code>getValue().getSomeProperty()</code> would not
	 * return <code>foo</code>!
	 * 
	 * @param <T>
	 * @param value
	 *            the value of the property field
	 * @param defaultValue
	 *            the default value, if no other value is non-null
	 * @param attribute
	 *            the component attribute being evaluated
	 * @param component
	 *            the component we are getting the attribute value for
	 * @param context
	 *            the faces context
	 * @return <code>value</code> , the result of evaluating the
	 *         {@link ValueExpression} for <code>attribute</code> on
	 *         <code>component</code> , or <code>defaultValue</code>.
	 * 
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getAttribute(T value, T defaultValue, String attribute,
			UIComponent component, FacesContext context) {
		if (!Utils.isEmpty(value)) {
			// XXX IF some object were being saved whose toString was the empty
			// string, this could produce incorrect results. However, as an
			// empty toString would not be best practice, I feel comfortable not
			// doing an instanceof, as it would kill performance. -n
			return value;
		}

		ValueExpression ve = component.getValueExpression(attribute);

		// evaluate the value expression
		return Utils.getValue((ve == null) ? null : (T) ve.getValue(context
				.getELContext()), defaultValue);
	}

	/**
	 * Should be called when {@link #broadcast(FacesEvent)} is called.
	 * 
	 * @param component
	 *            the component broadcast was called on.
	 * @param event
	 *            the event being broadcast
	 * @throws AbortProcessingException
	 */
	public static void broadcast(UIComponent component, FacesEvent event)
			throws AbortProcessingException {
		Utils.notNull(event, "event");
		AjaxContext ajax = AjaxContext.getCurrentInstance();
		if (ajax.isAjaxRequest()) {
			ajax.broadcast(component, event);
		}
	}

	/**
	 * Convenience method. Creates an iterator that returns the clientId of each
	 * component in components.
	 * 
	 * @param components
	 * @param context
	 * @return
	 */
	public static Iterator<String> getIdIterator(
			final Iterable<UIComponent> components, final FacesContext context) {

		return new Iterator<String>() {

			private Iterator<UIComponent> it = components.iterator();

			public boolean hasNext() {
				return it.hasNext();
			}

			public String next() {
				return it.next().getClientId(context);
			}

			public void remove() {
				throw new UnsupportedOperationException();
			}

		};
	}

	/**
	 * Attempts to resolve the given id relative to the given root component.
	 * This is accomplished by finding the nearest ancestor
	 * {@link NamingContainer} (or the {@link UIViewRoot}), prepending its ID
	 * and calling
	 * {@link UIComponent#invokeOnComponent(FacesContext, String, ContextCallback)}
	 * with the given ID. If a component is found and it is rendered, its ID is
	 * returned. Otherwise, the ID is returned without being changed.
	 * 
	 * @param id
	 * @param context
	 * @param root
	 * @return the id of the component whose id matches the relative id, or the
	 *         passed in id if one cannot be found.
	 */
	public static String resolveId(String id, FacesContext context,
			UIComponent root) {
		final String[] resolved = { id };

		// find the ancestor (if any) that is a naming container
		UIComponent c = root;
		while (c != null) {
			if (c instanceof NamingContainer) {
				id = c.getContainerClientId(context)
						+ NamingContainer.SEPARATOR_CHAR + id;
				break; // got the full id, so we're done
			}
			c = c.getParent();
		}

		// check that the 'full id' we just got corresponds to a real, rendered
		// component
		context.getViewRoot().invokeOnComponent(context, id,
				new ContextCallback() {
					public void invokeContextCallback(FacesContext context,
							UIComponent target) {
						if (target.isRendered()) {
							resolved[0] = target.getClientId(context);
						}
					}
				});

		return resolved[0];
	}

}
